home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / shells / rc-1.000 / rc-1 / rc-1.5-linux / history / history.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-07  |  6.4 KB  |  342 lines

  1. /*
  2.     history.c -- primitive history mechanism
  3.  
  4.     Paul Haahr & Byron Rakitzis, July 1991.
  5.  
  6.     This program mimics the att v8 = and == history programs.
  7.     The edit() algorithm was adapted from a similar program
  8.     that Boyd Roberts wrote, but otherwise all the code has
  9.     been written from scratch.
  10.  
  11.     edit() was subsequently redone by Hugh Redelmeier in order
  12.     to correctly deal with tab characters in the source line.
  13.  
  14.     BUGS:
  15.     There is an implicit assumption that commands are no
  16.     more than 1k characters long. 
  17. */
  18.  
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22.  
  23. static char *id = "@(#) history.c  8/91";
  24.  
  25. #undef FALSE
  26. #undef TRUE
  27. typedef enum { FALSE, TRUE } bool;
  28.  
  29. #define CHUNKSIZE 65536
  30.  
  31. static struct {
  32.     char *old, *new;
  33. } *replace;
  34.  
  35. static char **search, *progname, *history;
  36. static char me;    /* typically ':' or '-' */
  37. static bool editit = FALSE, printit = FALSE;
  38. static int nreplace = 0, nsearch = 0;
  39. static FILE *fp;
  40.  
  41. static void *ealloc(size_t n) {
  42.     void *p = (void *) malloc(n);
  43.     if (p == NULL) {
  44.         perror("malloc");
  45.         exit(1);
  46.     }
  47.     return p;
  48. }
  49.  
  50. static void *erealloc(void *p, size_t n) {
  51.     p = (void *) realloc(p, n);
  52.     if (p == NULL) {
  53.         perror("realloc");
  54.         exit(1);
  55.     }
  56.     return p;
  57. }
  58.  
  59. static char *newstr() {
  60.     return ealloc((size_t)1024);
  61. }
  62.  
  63. static char *basename(char *s) {
  64.     char *t = strrchr(s, '/');
  65.     return (t == NULL) ? s : t + 1;
  66. }
  67.  
  68. /* stupid O(n^2) substring matching routine */
  69.  
  70. static char *isin(char *target, char *pattern) {
  71.     size_t plen = strlen(pattern);
  72.     size_t tlen = strlen(target);
  73.     for (; tlen >= plen; target++, --tlen)
  74.         if (strncmp(target, pattern, plen) == 0)
  75.             return target;
  76.     return NULL;
  77. }
  78.  
  79. /* replace the first match in the string with "new" */
  80. static char *sub(char *s, char *old, char *new) {
  81.     char *t, *u;
  82.  
  83.     t = isin(s, old);
  84.     u = newstr();
  85.  
  86.     *t = '\0';
  87.     while (*old != '\0')
  88.         old++, t++;
  89.     strcpy(u, s);
  90.     strcat(u, new);
  91.     strcat(u, t);
  92.     return u;
  93. }
  94.  
  95. static char *edit(char *s) {
  96.     char *final, *f, *end;
  97.     int col;
  98.     bool ins;
  99.     
  100. start:
  101.     fprintf(stderr, "%s\n", s);    
  102.     f = final = newstr();
  103.     end = s + strlen(s);
  104.     col = 0;
  105.     ins = FALSE;
  106.     
  107.     for (;; col++) {
  108.         int    c = getchar();
  109.  
  110.         if (c == me && col == 0) {
  111.             int    peekc = getchar();
  112.             if (peekc == '\n')
  113.                 return NULL;
  114.             ungetc(peekc, stdin);
  115.         }
  116.         if (c == '\n') {
  117.             if (col == 0)
  118.                 return s;
  119.             
  120.             while (s < end) /* copy remainder of string */
  121.                 *f++ = *s++;
  122.             *f = '\0';
  123.             s = final;
  124.             goto start;
  125.         } else if (ins || s>=end) {
  126.             /* col need not be accurate -- tabs need not be interpreted */
  127.             *f++ = c;
  128.         } else {
  129.             switch (c) {
  130.             case '+':
  131.                 while (s < end)
  132.                     *f++ = *s++;
  133.                 *f = '\0';
  134.                 continue;
  135.             case '%':
  136.                 c = ' ';
  137.                 /* FALLTHROUGH */
  138.             default:
  139.                 *f++ = c;
  140.                 break;    
  141.             case EOF:
  142.                 exit(1);
  143.                 /* NOTREACHED */
  144.             case ' ':
  145.                 if (*s == '\t') {
  146.                     int    oldcol = col;
  147.  
  148.                     for (;; col++) {
  149.                         int    peekc;
  150.  
  151.                         if ((col&07) == 07) {
  152.                             *f++ = '\t';    /* we spaced past a tab */
  153.                             break;
  154.                         }
  155.                         peekc = getchar();
  156.                         if (peekc != ' ') {
  157.                             ungetc(peekc, stdin);
  158.                             if (peekc != '\n') {
  159.                                 /* we spaced partially into a tab */
  160.                                 do {
  161.                                     *f++ = ' ';
  162.                                     oldcol++;
  163.                                 } while (oldcol <= col);
  164.                             }
  165.                             break;
  166.                         }
  167.                     }
  168.                 } else {
  169.                     *f++ = *s;
  170.                 }
  171.                 break;
  172.             case '#':
  173.                 break;
  174.             case '$':
  175.                 end = s;    /* truncate s */
  176.                 continue;    /* skip incrementing s */
  177.             case '^':
  178.                 ins = TRUE;
  179.                 continue;    /* skip incrementing s */
  180.             case '\t':
  181.                 for (;; col++) {
  182.                     if ((*f++ = s<end? *s++ : '\t') == '\t') {
  183.                         col = col | 07;    /* advance to before next tabstop */
  184.                     }
  185.                     if ((col&07) == 07)    /* stop before tabstop */
  186.                         break;
  187.                 }
  188.                 continue;    /* skip incrementing s */
  189.             }
  190.             if (s<end && (*s!='\t' || (col&07)==07))
  191.                 s++;
  192.         }
  193.     }
  194. }
  195.  
  196. static char *readhistoryfile(char **last) {
  197.     char *buf;
  198.     size_t count, size;
  199.     long nread;
  200.  
  201.     if ((history = getenv("history")) == NULL) {
  202.         fprintf(stderr, "$history not set\n");
  203.         exit(1);
  204.     }
  205.     fp = fopen(history, "r+");
  206.     if (fp == NULL) {
  207.         perror(history);
  208.         exit(1);
  209.     }
  210.  
  211.     size = 0;
  212.     count = 0;
  213.     buf = ealloc(size = CHUNKSIZE);
  214.     while ((nread = fread(buf + count, sizeof (char), size - count, fp)) > 0) {
  215.         count += nread;
  216.         if (size - count == 0)
  217.             buf = erealloc(buf, size *= 4);
  218.     }
  219.     if (nread == -1) {
  220.         perror(history);
  221.         exit(1);
  222.     }
  223.     *last = buf + count;
  224.     return buf;
  225. }
  226.  
  227. static char *getcommand() {
  228.     char *s, *t;
  229.     static char *hist = NULL, *last;
  230.  
  231.     if (hist == NULL) {
  232.         hist = readhistoryfile(&last);
  233.         *--last = '\0';        /* trim final newline */
  234.     }
  235.  
  236. again:    s = last;
  237.     if (s < hist)
  238.         return NULL;
  239.     while (*--s != '\n')
  240.         if (s <= hist) {
  241.             last = hist - 1;
  242.             return hist;
  243.         }
  244.     *s = '\0';
  245.     last = s++;
  246.  
  247.     /*
  248.      * if the command contains the "me" character at the start of the line
  249.      * or after any of [`{|()@] then try again
  250.      */
  251.  
  252.     for (t = s; *t != '\0'; t++)
  253.         if (*t == me) {
  254.             char *u = t - 1;
  255.             while (u >= s && (*u == ' ' || *u == '\t'))
  256.                 --u;
  257.             if (u < s)
  258.                 goto again;
  259.             switch (*u) {
  260.             case '`': case '@':
  261.             case '(': case ')':
  262.             case '{': case '|':
  263.                 goto again;
  264.             default:
  265.                 break;
  266.             }
  267.         }
  268.     return s;
  269. }
  270.  
  271. int main(int argc, char **argv) {
  272.     int i;
  273.     char *s;
  274.  
  275.     s = progname = basename(argv[0]);
  276.     me = *s++;
  277.     if (*s == me) {
  278.         s++;
  279.         editit = TRUE;
  280.     }
  281.     if (*s == 'p') {
  282.         s++;
  283.         printit = TRUE;
  284.     }
  285. /* Nahh...
  286.     if (*s != '\0') {
  287.         fprintf(stderr, "\"%s\": bad name for history program\n", progname);
  288.         exit(1);
  289.     }
  290. */
  291.  
  292.     if (argc > 1) {
  293.         replace = ealloc((argc - 1) * sizeof *replace);
  294.         search = ealloc((argc - 1) * sizeof *search);
  295.     }
  296.     for (i = 1; i < argc; i++)
  297.         if ((s = strchr(argv[i], ':')) == NULL)
  298.             search[nsearch++] = argv[i];
  299.         else {
  300.             *(char *)s = '\0';    /* do we confuse ps too much? */
  301.             replace[nreplace].old = argv[i];
  302.             replace[nreplace].new = s + 1;
  303.             nreplace++;
  304.         }
  305.  
  306. next:    s = getcommand();
  307.     if (s == NULL) {
  308.         fprintf(stderr, "command not matched\n");
  309.         return 1;
  310.     }
  311.     for (i = 0; i < nsearch; i++)
  312.         if (!isin(s, search[i]))
  313.             goto next;
  314.     for (i = 0; i < nreplace; i++)
  315.         if (!isin(s, replace[i].old))
  316.             goto next;
  317.         else
  318.             s = sub(s, replace[i].old, replace[i].new);
  319.     if (editit) {
  320.         s = edit(s);
  321.         if (s == NULL)
  322.             goto next;
  323.     }
  324.     fseek(fp, 0, 2); /* 2 == end of file. i.e., append command to $history */
  325.     fprintf(fp, "%s\n", s);
  326.     fclose(fp);
  327.     if (printit)
  328.         printf("%s\n", s);
  329.     else {
  330.         char *shell = getenv("SHELL");
  331.  
  332.         if (!editit)
  333.             fprintf(stderr, "%s\n", s);
  334.         if (shell == NULL)
  335.             shell = "/bin/sh";
  336.         execl(shell, basename(shell), "-c", s, NULL);
  337.         perror(shell);
  338.         exit(1);
  339.     }
  340.     return 0;
  341. }
  342.